package com.rtsapp.naval.reloadagent.filemonitor;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

/**
 * 文件监视器
 */
public class FileMonitor implements Runnable {

	
    private final ArrayList<FolderWatcher> folders = new ArrayList<FolderWatcher>();
    private final List<FileAddedListener> fileAddedListeners;
    private final List<FileDeletedListener> fileDeletedListeners;
    private final List<FileModifiedListener> fileModifiedListeners;

    /**
     * @param 要监视的目录列表
     * @param 监视的文件扩展名
     */
    public FileMonitor( List<String> folderPaths, String fileExtension ) {
    	
        this.fileAddedListeners = new LinkedList<FileAddedListener>();
        this.fileDeletedListeners = new LinkedList<FileDeletedListener>();
        this.fileModifiedListeners = new LinkedList<FileModifiedListener>();
        
        for (String path : folderPaths) {
          File folder = new File(path);
          if ( !folder.isAbsolute() || !folder.isDirectory() ) {
              throw new IllegalArgumentException("The parameter with value "+ path + " MUST be a folder");
          }
          
          folders.add( new FolderWatcher(folder, fileExtension ) );
        }
    }

    public void run() {
        for ( FolderWatcher folder : folders ) {
            folder.checkDeletion();
            folder.checkAddAndModify();
        }
    }

    public void addModifiedListener(FileModifiedListener listener) {
        fileModifiedListeners.add(listener);
    }

    public void addDeletedListener(FileDeletedListener listener) {
        fileDeletedListeners.add(listener);
    }

    public void addAddedListener(FileAddedListener listener) {
        fileAddedListeners.add(listener);
    }

    private void notifyModifiedListeners(FileEvent event) {
        for (FileModifiedListener listener : fileModifiedListeners) {
            listener.fileModified(event);
        }
    }

    private void notifyAddedListeners(FileEvent event) {
        for (FileAddedListener listener : fileAddedListeners) {
            listener.fileAdded(event);
        }
    }

    private void notifyDeletedListeners(FileEvent event) {
        for (FileDeletedListener listener : fileDeletedListeners) {
            listener.fileDeleted(event);
        }
    }
    
    

    private class FolderWatcher {
    	
    	private final FilenameFilter filenameFilter;
        private final File folder;
        private final HashMap<File, Long> fileMap = new HashMap<File, Long>();
        
        private FolderWatcher(File folder, final String fileExtension ) {
          
        	this.folder = folder;
          
            filenameFilter = new FilenameFilter(  ) {
                public boolean accept(File folder, String name) {
                    return name.endsWith(fileExtension)
                            || new File( folder.getAbsolutePath() + File.separator + name )
                                    .isDirectory();
                }
            };
            
        }

        /**
         * 检查删除的文件
         */
        protected void checkDeletion() {
            List<File> filesToDelete = new LinkedList<File>();
            for (File file : fileMap.keySet()) {
                if ( !file.exists() ) {
                    filesToDelete.add(file);
                    notifyDeletedListeners(new FileEvent(folder, file));
                }
            }
            
            for (File file : filesToDelete) {
                fileMap.remove(file);
            }
        }

        /**
         * 检查新添加和修改的文件
         */
        protected void checkAddAndModify() {
            checkAddAndModify( folder );
        }
        
        /**
         * 检查文件的新增和修改
         * @param 当前需要监视的目录
         */
        protected void checkAddAndModify(File currentFolder) {
            for (File file : getFiles(currentFolder)) {
                if (file.isDirectory()) {
                    checkAddAndModify(file);
                } else {
                    Long lastModified = fileMap.get(file);
                    if (lastModified != null) {
                        if (lastModified != file.lastModified()) {
                            fileMap.put(file, file.lastModified());
                            notifyModifiedListeners(new FileEvent(folder, file));
                        }
                    } else {
                        fileMap.put(file, file.lastModified());
                        notifyAddedListeners(new FileEvent(folder, file));
                    }
                }
            }
        }

        
        public File[] getFiles( File folder ) {
            return folder.listFiles(filenameFilter);
        }
        
    }
}
